﻿' ----------------------------------------------------------------------
'
' FileInfo.vb
'
'   Dbfs.Domain
'
'   This class contains the implementation of a single folder.
'
' ----------------------------------------------------------------------
' version:  1.0.0.1
' today:    06/10/10  09:47:00
' ident:    "@(#)/Dbfs/Domain/FileInfo.vb 1.0 0.1 06/10/10 0 rjs"

Imports System.IO
Imports System.Net
Imports System.Security.AccessControl
Imports Dbfs.Data
Imports Dbfs.Security


Namespace Dbfs

    ''' <summary>
    ''' (DBFS) Exposes instance methods for creating, moving, and enumerating through 
    ''' directories and subdirectories. This class cannot be inherited.
    ''' </summary>
    ''' <remarks>Use the DirectoryInfo class for typical operations such as 
    ''' copying, moving, renaming, creating, and deleting directories.</remarks>
    <SerializableAttribute()> _
    Public Class FileInfo
        Inherits FileSystemInfo
        Implements IDbfsNode

        ''' <summary>
        ''' The <see cref="FileSystemEntity"/> as cast to a <see cref="FileEntity"/>.
        ''' </summary>
        ''' <remarks></remarks>
        Private _entity As FileEntity


#Region " Enumerations "
        Public Enum FileScheme As Integer
            Immed = 0
            File = 1
            SqlFile = 2
            Url = 3
        End Enum
#End Region ' Enumerations

#Region " Constructors "
        ''' <summary>
        ''' Initializes a new instance of the <see cref="FileInfo"/> class.
        ''' </summary>
        ''' <param name="path">A string specifying the path on which to create the <b>DirectoryInfo</b>.</param>
        ''' <remarks>If the path is fully qualified with a UNC name, a folder is created that
        ''' can be persisted in that filesystem.  If the path does not contain a UNC name,
        ''' a folder is created within the virtual space for the current user.</remarks>
        Public Sub New(ByVal path As String)
            MyBase.New(path)
            Entity = Context.Read(Of FileEntity)(path)
            _entity = Entity
        End Sub
#End Region ' Constructors

#Region " Properties "
        ''' <summary>
        ''' Gets the parent directory of a specified subdirectory.
        ''' </summary>
        ''' <value>The parent directory, or a null reference (<b>Nothing</b> in Visual Basic) 
        ''' if the path is null or if the file path denotes a root 
        ''' (such as "\", "C:", or * "\\server\share").</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Directory() As DirectoryInfo
            Get
                If ParentInfo Is Nothing Then
                    ParentInfo = New DirectoryInfo(Path.GetDirectoryName(FullName))
                End If
                Return ParentInfo
            End Get
        End Property

        ''' <summary>
        ''' Gets the size, in bytes, of the current file.
        ''' </summary>
        ''' <value>The size of the current file in bytes.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Length() As Long
            Get
                Return _entity.Length
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the name of this folder.
        ''' </summary>
        ''' <value>A String containing the name for this folder.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property Name() As String
            Get
                Return FileSystemItem.Name.Value
            End Get
        End Property

        ''' <summary>
        ''' Gets the short 8.3 version of the name for this folder.
        ''' </summary>
        ''' <value></value>
        ''' <returns>A String containing the 8.3 name for this folder.</returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property ShortName() As String 'Implements IDbfsNodeItem.ShortName
            Get
                Return FileSystemItem.Name.ShortName
            End Get
        End Property
#End Region ' Properties

#Region " Public Methods "
        ''' <summary>
        ''' Creates a file.
        ''' </summary>
        ''' <remarks>If the file already exists, this method does nothing.</remarks>
        Public Sub Create()
            ' if the file already exists, this method does nothing
            If Exists Then Return
            ' add this item to the database
            Context.Create(Entity)
        End Sub

        ''' <summary>
        ''' Creates a <see cref="StreamWriter"/> that writes a new text file.
        ''' </summary>
        ''' <returns>A new <b>StreamWriter</b>.</returns>
        ''' <remarks>By default, full read/write access to new files is granted to all users.</remarks>
        Public Function CreateText() As StreamWriter
            Return _entity.CreateText
        End Function

        ''' <summary>
        ''' Copies an existing file to a new file, disallowing the overwriting of an existing file.
        ''' </summary>
        ''' <param name="destinationName">The name of the new file to copy to.</param>
        ''' <returns>A new file with a fully qualified path.</returns>
        ''' <remarks></remarks>
        Public Function CopyTo(ByVal destinationName As String) As FileInfo
            ValidateArguments(destinationName)
            Dim fi As New FileInfo(destinationName)
            _entity.CopyTo(fi.Entity)
            fi.Create()
            Return fi
        End Function

        ''' <summary>
        ''' Deletes this <see cref="DirectoryInfo"/> if it is empty.
        ''' </summary>
        ''' <remarks></remarks>
        Public Overrides Sub Delete()
            Context.Delete(Entity)
        End Sub

        ''' <summary>
        ''' Gets a <see cref="FileSecurity"/> object that encapsulates the 
        ''' access control list (ACL) entries for the directory described by the 
        ''' current <see cref="FileInfo"/> object.
        ''' </summary>
        ''' <returns>A <see cref="FileSecurity"/> object that encapsulates 
        ''' the access control rules for the directory.</returns>
        ''' <remarks></remarks>
        Public Overrides Function GetAccessControl() As Security.FileSystemSecurity
            ' apply security, check to see if we can read the access control list
            If (AccessRights And FileSystemRights.ReadPermissions) = 0 Then
                Throw New DbfsException("Access Denied")
            End If
            If Entity.FileSystemSecurity Is Nothing Then
                Throw New SystemException("Invalid security")
            End If
            Return Entity.FileSystemSecurity
        End Function

        ''' <summary>
        ''' Moves a <see cref="DirectoryInfo"/> instance and its contents to a new path.
        ''' </summary>
        ''' <param name="destDirName">The name and path to which to move this directory. 
        ''' The destination cannot be another disk volume or a directory with the identical name. 
        ''' It can be an existing directory to which you want to add this directory as a subdirectory.</param>
        ''' <remarks>This method permits moving a directory to a read-only directory. 
        ''' The read/write attribute of neither directory is affected.</remarks>
        Public Sub MoveTo(ByVal destDirName As String)


            ' temp
            Dim moveObject As DirectoryInfo
            Dim newName As String = destDirName

            Dim targetDir As DirectoryInfo


            ' validate arguments
            If destDirName Is Nothing Then _
                Throw New ArgumentNullException("destDirName")

            If destDirName.Trim.Length = 0 Or destDirName.IndexOfAny(System.IO.Path.GetInvalidPathChars()) <> -1 Then _
                Throw New ArgumentException("destDirName")

            ' enforce source security
            If (AccessRights And FileSystemRights.Delete) = 0 AndAlso _
               (Directory.AccessRights And FileSystemRights.DeleteSubdirectoriesAndFiles) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' resolve any relative pathing
            'destDirName = Directory.ResolvePath(Parent.FullName, destDirName)

            ' see if the destination already exists in the filesystem
            targetDir = New DirectoryInfo(destDirName)

            ' if target does not exist, we move this folder into the parent
            If Not targetDir.Exists Then _
                targetDir = targetDir.Parent

            ' enforce target security
            If targetDir.AccessRights And FileSystemRights.CreateDirectories <> FileSystemRights.CreateDirectories Then
                Throw New DbfsException("Access Denied")
            End If

            ' check for the simple case of just renaming the current folder
            'If Directory.FullPath = targetDir.FullPath Then
            'Rename(Path.GetFileName(destDirName))
            'Return
            'End If




            ' if changes are being tracked, we need to create a new object
            If TrackChanges Then
                Dim t As Date = Now()
                moveObject = Me.Clone
                moveObject.CreationTime = t
                moveObject.LastAccessTime = t
                moveObject.LastWriteTime = t
                Me.Delete()
            Else
                'moveObject = Me
                'Me.Parent.Children.Remove(Me)
            End If

            'targetDir.Children.Add(moveObject)


            ' if changes are being tracked, we need to mark the old record as
            ' invalid and get a new layer of history
            If TrackChanges Then
                ' we'll need a new layer of history in the database
                Dim newHistoryLayer As DirectoryInfo = Me.Clone
                ' this layer represents the 'old' me
                'newHistoryLayer.IsDeleted = True
                ' the 'new' me doesn't exist in the database yet
                'Entity._exists = False
            End If

            'If oldPath <> newPath Then
            ' remove this from the current parent
            If Directory IsNot Nothing Then
                'Directory.Children.Remove(Me)
            End If

            ParentInfo = Nothing
            'Entity._parentNodeID = -1
            'End If



            ' make changes to the 'new' me
            'Entity._name.Value = newName
            'FileSystemItem._name._shortName = Filename.ShortNameOf(newName, nameArr)
            'FullPath = oldDatabaseName & newPath & DIRECTORY_SEP_CHAR & newName
            ' ?????
            'OriginalPath = FullPath

        End Sub

        ''' <summary>
        ''' Creates a read-only <see cref="FileStream"/>.
        ''' </summary>
        ''' <returns>A new read-only <see cref="FileStream"/> object.</returns>
        ''' <remarks></remarks>
        Public Function OpenRead() As Stream
            Return _entity.OpenRead
        End Function

        Public Function OpenWrite() As Stream
            Return _entity.OpenWrite
        End Function

        Public Overrides Sub Refresh()
            Entity = Context.Read(Of FileEntity)(Entity)
        End Sub

        ''' <summary>
        ''' Applies access control list (ACL) entries described by a <see cref="FileSecurity"/>
        ''' object to the file described by the current <see cref="FileInfo"/> object.
        ''' </summary>
        ''' <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes 
        ''' an access control list (ACL) entry to apply to the current file.</param>
        ''' <remarks></remarks>
        Public NotOverridable Overrides Sub SetAccessControl(ByVal fileSecurity As System.Security.AccessControl.FileSystemSecurity)
            Dim mySecurity As DbfsFileSecurity = New DbfsFileSecurity(fileSecurity)
            MyBase.SetAccessControl(mySecurity)
        End Sub

        ''' <summary>
        ''' Returns the path as a string.
        ''' </summary>
        ''' <returns>A string representing the path.</returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Return OriginalPath
        End Function
#End Region ' Public Methods

#Region " Private Methods "
        ''' <summary>
        ''' Helper function to perform a simple rename of the current directory.
        ''' </summary>
        ''' <param name="newName">A String specifying the new folder name.</param>
        ''' <remarks></remarks>
        Private Sub Rename(ByVal newName As String)

            ' solve for the easy case first (changes are not being tracked)
            If Not TrackChanges Then
                SetName(newName)
                Return
            End If

            ' if changes are being tracked, we need to mark the old record as
            ' invalid and get a new layer of history

            ' first, get a new layer of history in the database
            Dim newHistoryLayer As FileSystemItem = FileSystemItem.Clone

            ' 'delete' the current layer
            Dim utc As Date = Date.UtcNow()

            ' set the new layer of history as the current layer
            'Entity = newHistoryLayer
            CreationTimeUtc = utc
            LastAccessTimeUtc = utc
            'Name = newName

            FullPath = Directory.Name & DIRECTORY_SEP_CHAR & newName
            'OriginalPath = FullPath

        End Sub

        ''' <summary>
        ''' Validates the arguments for 
        ''' copyto... .
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub ValidateArguments(ByVal destFileName As String)

            ' validate parameters
            If destFileName Is Nothing Then
                Throw New ArgumentNullException("destFileName")
            End If

            If destFileName.Trim() = "" Then
                Throw New ArgumentException("Invalid filename.")
            End If

            If destFileName.IndexOfAny(Path.GetInvalidPathChars()) > 0 Then
                Throw New ArgumentException("Invalid filename.")
            End If

            If destFileName.IndexOf(":"c) <> -1 Then
                Throw New NotSupportedException("Path cannot specify a different volume.")
            End If

        End Sub
#End Region ' Private Methods

    End Class

End Namespace
